{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "tfrecord.ipynb",
      "version": "0.3.2",
      "provenance": [],
      "private_outputs": true,
      "collapsed_sections": [
        "pL--_KGdYoBz"
      ],
      "toc_visible": true
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.6.5"
    },
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "pL--_KGdYoBz"
      },
      "source": [
        "##### Copyright 2018 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "cellView": "both",
        "colab_type": "code",
        "id": "uBDvXpYzYnGj",
        "colab": {}
      },
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "HQzaEQuJiW_d"
      },
      "source": [
        "# TFRecords と `tf.Example` の使用法\n",
        "\n",
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://www.tensorflow.org/tutorials/load_data/tfrecord\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\" />View on TensorFlow.org</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs/blob/master/site/ja/tutorials/load_data/tfrecord.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Run in Google Colab</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://github.com/tensorflow/docs/blob/master/site/ja/tutorials/load_data/tfrecord.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />View source on GitHub</a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "RSywPQ2n736s"
      },
      "source": [
        "Note: これらのドキュメントは私たちTensorFlowコミュニティが翻訳したものです。コミュニティによる 翻訳は**ベストエフォート**であるため、この翻訳が正確であることや[英語の公式ドキュメント](https://www.tensorflow.org/?hl=en)の 最新の状態を反映したものであることを保証することはできません。 この翻訳の品質を向上させるためのご意見をお持ちの方は、GitHubリポジトリ[tensorflow/docs](https://github.com/tensorflow/docs)にプルリクエストをお送りください。 コミュニティによる翻訳やレビューに参加していただける方は、 [docs-ja@tensorflow.org メーリングリスト](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs-ja)にご連絡ください。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "3pkUd_9IZCFO"
      },
      "source": [
        "データの読み込みを効率的にするには、データをシリアライズし、連続的に読み込めるファイルのセット（各ファイルは 100-200MB）に保存することが有効です。データをネットワーク経由で流そうとする場合には、特にそうです。また、データの前処理をキャッシングする際にも役立ちます。\n",
        "\n",
        "TFRecord 形式は、バイナリレコードのシリーズを保存するための単純な形式です。\n",
        "\n",
        "[プロトコルバッファ](https://developers.google.com/protocol-buffers/) は、構造化データを効率的にシリアライズする、プラットフォームや言語に依存しないライブラリです。\n",
        "\n",
        "プロトコルメッセージは `.proto` という拡張子のファイルで表されます。メッセージの型を識別するもっとも簡単な方法です。\n",
        "\n",
        "`tf.Example` メッセージ（あるいはプロトコルバッファ）は、`{\"string\": value}` 形式のマッピングを表現する柔軟なメッセージ型です。これは、TensorFlow 用に設計され、[TFX](https://www.tensorflow.org/tfx/) のような上位レベルの API で共通に使用されています。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Ac83J0QxjhFt"
      },
      "source": [
        "このノートブックでは、`tf.Example` メッセージの作成、解析と使用法をデモし、その後、`tf.Example` メッセージをシリアライズして `.tfrecord` に書き出し、その後読み取る方法を示します。\n",
        "\n",
        "注：こうした構造は有用ですが必ずそうしなければならなというものではありません。[`tf.data`](https://www.tensorflow.org/guide/datasets) を使っていて、それでもデータの読み込みが訓練のボトルネックであるという場合でなければ、既存のコードを TFRecords を使用するために変更する必要はありません。データセットの性能改善のヒントは、 [Data Input Pipeline Performance](https://www.tensorflow.org/guide/performance/datasets) を参照ください。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "WkRreBf1eDVc"
      },
      "source": [
        "## 設定"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "Ja7sezsmnXph",
        "colab": {}
      },
      "source": [
        "from __future__ import absolute_import, division, print_function, unicode_literals\n",
        "\n",
        "try:\n",
        "  # Colab only\n",
        "  %tensorflow_version 2.x\n",
        "except Exception:\n",
        "    pass\n",
        "import tensorflow as tf\n",
        "\n",
        "import numpy as np\n",
        "import IPython.display as display"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "e5Kq88ccUWQV"
      },
      "source": [
        "## `tf.Example`"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "VrdQHgvNijTi"
      },
      "source": [
        "###  `tf.Example` 用のデータ型"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "lZw57Qrn4CTE"
      },
      "source": [
        "基本的には `tf.Example` は `{\"string\": tf.train.Feature}` というマッピングです。\n",
        "\n",
        "`tf.train.Feature` メッセージ型は次の3つの型のうち1つをとることができます。（[.proto file](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/example/feature.proto) を参照）このほかの一般的なデータ型のほとんどは、強制的にこれらのうちの1つにすること可能です。\n",
        "\n",
        "1. `tf.train.BytesList` (次の型のデータを扱うことが可能)\n",
        "\n",
        "  - `string`\n",
        "  - `byte`  \n",
        "\n",
        "1. `tf.train.FloatList` (次の型のデータを扱うことが可能)\n",
        "\n",
        "  - `float` (`float32`)\n",
        "  - `double` (`float64`)\n",
        "\n",
        "1. `tf.train.Int64List` (次の型のデータを扱うことが可能)\n",
        "\n",
        "  - `bool`\n",
        "  - `enum`\n",
        "  - `int32`\n",
        "  - `uint32`\n",
        "  - `int64`\n",
        "  - `uint64`"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "_e3g9ExathXP"
      },
      "source": [
        "通常の TensorFlow の型を `tf.Example` 互換の `tf.train.Feature` に変換するには、次のショートカット関数を使うことができます。\n",
        "\n",
        "どの関数も、1個のスカラー値を入力とし、上記の3つの `list` 型のうちの一つを含む `tf.train.Feature` を返します。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "mbsPOUpVtYxA",
        "colab": {}
      },
      "source": [
        "# 下記の関数を使うと値を tf.Example と互換性の有る型に変換できる\n",
        "\n",
        "def _bytes_feature(value):\n",
        "  \"\"\"string / byte 型から byte_list を返す\"\"\"\n",
        "  if isinstance(value, type(tf.constant(0))):\n",
        "    value = value.numpy() # BytesList won't unpack a string from an EagerTensor.\n",
        "  return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))\n",
        "\n",
        "def _float_feature(value):\n",
        "  \"\"\"float / double 型から float_list を返す\"\"\"\n",
        "  return tf.train.Feature(float_list=tf.train.FloatList(value=[value]))\n",
        "\n",
        "def _int64_feature(value):\n",
        "  \"\"\"bool / enum / int / uint 型から Int64_list を返す\"\"\"\n",
        "  return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Wst0v9O8hgzy"
      },
      "source": [
        "注：単純化のため、このサンプルではスカラー値の入力のみを扱っています。スカラー値ではない特徴を扱うもっとも簡単な方法は、`tf.serialize_tensor` を使ってテンソルをバイナリ文字列に変換する方法です。TensorFlow では文字列はスカラー値として扱います。バイナリ文字列をテンソルに戻すには、`tf.parse_tensor` を使用します。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "vsMbkkC8xxtB"
      },
      "source": [
        "上記の関数の使用例を下記に示します。入力がさまざまな型であるのに対して、出力が標準化されていることに注目してください。入力が、強制変換できない型であった場合、例外が発生します。（例： `_int64_feature(1.0)` はエラーとなります。`1.0` が浮動小数点数であるためで、代わりに `_float_feature` 関数を使用すべきです）"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "hZzyLGr0u73y",
        "colab": {}
      },
      "source": [
        "print(_bytes_feature(b'test_string'))\n",
        "print(_bytes_feature(u'test_bytes'.encode('utf-8')))\n",
        "\n",
        "print(_float_feature(np.exp(1)))\n",
        "\n",
        "print(_int64_feature(True))\n",
        "print(_int64_feature(1))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "nj1qpfQU5qmi"
      },
      "source": [
        "主要なメッセージはすべて `.SerializeToString` を使ってバイナリ文字列にシリアライズすることができます。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "5afZkORT5pjm",
        "colab": {}
      },
      "source": [
        "feature = _float_feature(np.exp(1))\n",
        "\n",
        "feature.SerializeToString()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "laKnw9F3hL-W"
      },
      "source": [
        "### `tf.Example` メッセージの作成"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "b_MEnhxchQPC"
      },
      "source": [
        "既存のデータから `tf.Example` を作成したいとします。実際には、データセットの出処はどこでもよいのですが、1件の観測記録から`tf.Example` メッセージを作る手順はおなじです。\n",
        "\n",
        "1. 観測記録それぞれにおいて、各値は上記の関数を使って3種類の互換性のある型からなる `tf.train.Feature` に変換する必要があります。\n",
        "\n",
        "1. 次に、特徴の名前を表す文字列と、#1 で作ったエンコード済みの特徴量を対応させたマップ（ディクショナリ）を作成します。\n",
        "\n",
        "1. #2 で作成したマップを[特徴量メッセージ](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/example/feature.proto#L85)に変換します。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "4EgFQ2uHtchc"
      },
      "source": [
        "このノートブックでは、NumPy を使ってデータセットを作成します。\n",
        "\n",
        "このデータセットには4つの特徴量があります。\n",
        "- `False` または `True` を表す論理値。出現確率は等しいものとします。\n",
        "- ランダムなバイト値。全体において一様であるとします。\n",
        "- `[-10000, 10000)` の範囲から一様にサンプリングした整数値。\n",
        "- 標準正規分布からサンプリングした浮動小数点数。\n",
        "\n",
        "サンプルは上記の分布から独立しておなじ様に分布した10,000件の観測記録からなるものとします。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "CnrguFAy3YQv",
        "colab": {}
      },
      "source": [
        "# データセットに含まれる観測結果の件数\n",
        "n_observations = int(1e4)\n",
        "\n",
        "# ブール特徴量 False または True としてエンコードされている\n",
        "feature0 = np.random.choice([False, True], n_observations)\n",
        "\n",
        "# 整数特徴量  -10000 から 10000 の間の乱数\n",
        "feature1 = np.random.randint(0, 5, n_observations)\n",
        "\n",
        "# バイト特徴量\n",
        "strings = np.array([b'cat', b'dog', b'chicken', b'horse', b'goat'])\n",
        "feature2 = strings[feature1]\n",
        "\n",
        "# 浮動小数点数特徴量 標準正規分布から発生\n",
        "feature3 = np.random.randn(n_observations)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "aGrscehJr7Jd"
      },
      "source": [
        "これらの特徴量は、`_bytes_feature`, `_float_feature`, `_int64_feature` のいずれかを使って `tf.Example` 互換の型に強制変換されます。その後、エンコード済みの特徴量から `tf.Example` メッセージを作成できます。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "RTCS49Ij_kUw",
        "colab": {}
      },
      "source": [
        "def serialize_example(feature0, feature1, feature2, feature3):\n",
        "  \"\"\"\n",
        "  ファイル出力可能な tf.Example メッセージを作成する\n",
        "  \"\"\"\n",
        "  \n",
        "  # 特徴量名と tf.Example 互換データ型との対応ディクショナリを作成\n",
        "  \n",
        "  feature = {\n",
        "      'feature0': _int64_feature(feature0),\n",
        "      'feature1': _int64_feature(feature1),\n",
        "      'feature2': _bytes_feature(feature2),\n",
        "      'feature3': _float_feature(feature3),\n",
        "  }\n",
        "  \n",
        "  # tf.train.Example を用いて特徴メッセージを作成\n",
        "  \n",
        "  example_proto = tf.train.Example(features=tf.train.Features(feature=feature))\n",
        "  return example_proto.SerializeToString()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "XftzX9CN_uGT"
      },
      "source": [
        "たとえば、データセットに `[False, 4, bytes('goat'), 0.9876]` という1つの観測記録があるとします。`create_message()` を使うとこの観測記録から `tf.Example` メッセージを作成し印字できます。上記のように、観測記録一つ一つが `Features` メッセージとして書かれています。`tf.Example` [メッセージ](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/example/example.proto#L88)は、この `Features` メッセージを包むラッパーに過ぎないことに注意してください。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "N8BtSx2RjYcb",
        "colab": {}
      },
      "source": [
        "# データセットからの観測記録の例\n",
        "\n",
        "example_observation = []\n",
        "\n",
        "serialized_example = serialize_example(False, 4, b'goat', 0.9876)\n",
        "serialized_example"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "_pbGATlG6u-4"
      },
      "source": [
        "メッセージをデコードするには、`tf.train.Example.FromString` メソッドを使用します。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "dGim-mEm6vit",
        "colab": {}
      },
      "source": [
        "example_proto = tf.train.Example.FromString(serialized_example)\n",
        "example_proto"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "y-Hjmee-fbLH"
      },
      "source": [
        "## TFRecord フォーマットの詳細\n",
        "\n",
        "TFRecord ファイルにはレコードのシーケンスが含まれます。このファイルはシーケンシャル読み取りのみが可能です。\n",
        "\n",
        "それぞれのレコードには、データを格納するためのバイト文字列とデータ長、そして整合性チェックのための CRC32C（Castagnoli 多項式を使った 32 ビットの CRC ）ハッシュ値が含まれます。\n",
        "\n",
        "各レコードのフォーマットは\n",
        "\n",
        "    uint64 長さ\n",
        "    uint32 長さのマスク済み crc32 ハッシュ値\n",
        "    byte   data[長さ]\n",
        "    uint32 データのマスク済み crc32 ハッシュ値\n",
        "\n",
        "複数のレコードが結合されてファイルを構成します。CRC については[ここ](https://en.wikipedia.org/wiki/Cyclic_redundancy_check)に說明があります。CRC のマスクは下記のとおりです。\n",
        "\n",
        "    masked_crc = ((crc >> 15) | (crc << 17)) + 0xa282ead8ul\n",
        "    \n",
        "注：TFRecord ファイルを作るのに、`tf.Example` を使わなければならないということはありません。tf.Example は、ディクショナリをバイト文字列にシリアライズする方法の1つです。エンコードされた画像データや、（`tf.io.serialize_tensor` を使ってシリアライズされ、`tf.io.parse_tensor` で読み込まれる）シリアライズされたテンソルもあります。そのほかのオプションについては、`tf.io` モジュールを参照してください。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "LYnQzvAvfchQ"
      },
      "source": [
        "## `tf.data` を使用した TFRecord ファイル"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "GmehkCCT81Ez"
      },
      "source": [
        "`tf.data` モジュールには、TensorFlow でデータを読み書きするツールが含まれます。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "1FISEuz8ubu3"
      },
      "source": [
        "### TFRecord ファイルの書き出し\n",
        "\n",
        "データをデータセットにするもっとも簡単な方法は `from_tensor_slices` メソッドです。\n",
        "\n",
        "配列に適用すると、このメソッドはスカラー値のデータセットを返します。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "mXeaukvwu5_-",
        "colab": {}
      },
      "source": [
        "tf.data.Dataset.from_tensor_slices(feature1)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "f-q0VKyZvcad"
      },
      "source": [
        "配列のタプルに適用すると、タプルのデータセットが返されます。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "H5sWyu1kxnvg",
        "colab": {}
      },
      "source": [
        "features_dataset = tf.data.Dataset.from_tensor_slices((feature0, feature1, feature2, feature3))\n",
        "features_dataset"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "m1C-t71Nywze",
        "colab": {}
      },
      "source": [
        "#  データセットから1つのサンプルだけを取り出すには `take(1)` を使います。\n",
        "for f0,f1,f2,f3 in features_dataset.take(1):\n",
        "  print(f0)\n",
        "  print(f1)\n",
        "  print(f2)\n",
        "  print(f3)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "mhIe63awyZYd"
      },
      "source": [
        "`Dataset` のそれぞれの要素に関数を適用するには、`tf.data.Dataset.map` メソッドを使用します。\n",
        "\n",
        "マップされる関数は TensorFlow のグラフモードで動作する必要があります。関数は `tf.Tensors` を処理し、返す必要があります。`create_example` のような非テンソル関数は、互換性のため `tf.py_func` でラップすることができます。\n",
        "\n",
        "`tf.py_func` を使用する際には、シェイプと型は取得できないため、指定する必要があります。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "apB5KYrJzjPI",
        "colab": {}
      },
      "source": [
        "def tf_serialize_example(f0,f1,f2,f3):\n",
        "  tf_string = tf.py_function(\n",
        "    serialize_example, \n",
        "    (f0,f1,f2,f3),  # 上記の関数にこれらの引数を渡す\n",
        "    tf.string)      # 戻り値の型は tf.string\n",
        "  return tf.reshape(tf_string, ()) # 結果はスカラー"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "mckRzbHlfchm",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "tf_serialize_example(f0,f1,f2,f3)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "CrFZ9avE3HUF"
      },
      "source": [
        "この関数をデータセットのそれぞれの要素に適用します。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "VDeqYVbW3ww9",
        "colab": {}
      },
      "source": [
        "serialized_features_dataset = features_dataset.map(tf_serialize_example)\n",
        "serialized_features_dataset"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "CRtx4Cjpfch2",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "def generator():\n",
        "  for features in features_dataset:\n",
        "    yield serialize_example(*features)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "sDl1JG09fch4",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "serialized_features_dataset = tf.data.Dataset.from_generator(\n",
        "    generator, output_types=tf.string, output_shapes=())"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "_ZVqJdH5fch6",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "serialized_features_dataset"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "p6lw5VYpjZZC"
      },
      "source": [
        "TFRecord ファイルに書き出します。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "vP1VgTO44UIE",
        "colab": {}
      },
      "source": [
        "filename = 'test.tfrecord'\n",
        "writer = tf.data.experimental.TFRecordWriter(filename)\n",
        "writer.write(serialized_features_dataset)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "6aV0GQhV8tmp"
      },
      "source": [
        "### TFRecord ファイルの読み込み"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "o3J5D4gcSy8N"
      },
      "source": [
        "`tf.data.TFRecordDataset` クラスを使って TFRecord ファイルを読み込むこともできます。\n",
        "\n",
        "`tf.data` を使って TFRecord ファイルを取り扱う際の詳細については、[こちら](https://www.tensorflow.org/guide/datasets#consuming_tfrecord_data)を参照ください。 \n",
        "\n",
        "`TFRecordDataset` を使うことは、入力データを標準化し、パフォーマンスを最適化するのに有用です。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "6OjX6UZl-bHC",
        "colab": {}
      },
      "source": [
        "filenames = [filename]\n",
        "raw_dataset = tf.data.TFRecordDataset(filenames)\n",
        "raw_dataset"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "6_EQ9i2E_-Fz"
      },
      "source": [
        "この時点で、データセットにはシリアライズされた `tf.train.Example` メッセージが含まれています。データセットをイテレートすると、スカラーの文字列テンソルが返ってきます。\n",
        "\n",
        "`.take` メソッドを使って最初の 10 レコードだけを表示します。\n",
        "\n",
        "注：`tf.data.Dataset` をイテレートできるのは、Eager Execution が有効になっている場合のみです。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "hxVXpLz_AJlm",
        "colab": {}
      },
      "source": [
        "for raw_record in raw_dataset.take(10):\n",
        "    print(repr(raw_record))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "W-6oNzM4luFQ"
      },
      "source": [
        "これらのテンソルは下記の関数でパースできます。\n",
        "\n",
        "注：ここでは、`feature_description` が必要です。データセットはグラフ実行を使用するため、この記述を使ってシェイプと型を構築するのです。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "zQjbIR1nleiy",
        "colab": {}
      },
      "source": [
        "# 特徴の記述\n",
        "feature_description = {\n",
        "    'feature0': tf.io.FixedLenFeature([], tf.int64, default_value=0),\n",
        "    'feature1': tf.io.FixedLenFeature([], tf.int64, default_value=0),\n",
        "    'feature2': tf.io.FixedLenFeature([], tf.string, default_value=''),\n",
        "    'feature3': tf.io.FixedLenFeature([], tf.float32, default_value=0.0),\n",
        "}\n",
        "\n",
        "def _parse_function(example_proto):\n",
        "  # 上記の記述を使って入力の tf.Example を処理\n",
        "  return tf.io.parse_single_example(example_proto, feature_description)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "gWETjUqhEQZf"
      },
      "source": [
        "あるいは、`tf.parse example` を使ってバッチ全体を一度にパースします。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "AH73hav6Bnmg"
      },
      "source": [
        "`tf.data.Dataset.map` メソッドを使って、データセットの各アイテムにこの関数を適用します。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "6Ob7D-zmBm1w",
        "colab": {}
      },
      "source": [
        "parsed_dataset = raw_dataset.map(_parse_function)\n",
        "parsed_dataset "
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "sNV-XclGnOvn"
      },
      "source": [
        "Eager Execution を使ってデータセット中の観測記録を表示します。このデータセットには 10,000 件の観測記録がありますが、最初の 10 個だけ表示します。  \n",
        "データは特徴量のディクショナリの形で表示されます。それぞれの項目は `tf.Tensor` であり、このテンソルの `numpy` 要素は特徴量を表します。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "x2LT2JCqhoD_",
        "colab": {}
      },
      "source": [
        "for parsed_record in parsed_dataset.take(10):\n",
        "  print(repr(raw_record))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Cig9EodTlDmg"
      },
      "source": [
        "ここでは、`tf.parse_example` が`tf.Example` のフィールドを通常のテンソルに展開しています。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "jyg1g3gU7DNn"
      },
      "source": [
        "## tf.python_io を使った TFRecord ファイル"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "3FXG3miA7Kf1"
      },
      "source": [
        "`tf.python_io` モジュールには、TFRecord ファイルの読み書きのための純粋な Python 関数も含まれています。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "CKn5uql2lAaN"
      },
      "source": [
        "### TFRecord ファイルの書き出し"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "LNW_FA-GQWXs"
      },
      "source": [
        "次にこの 10,000 件の観測記録を `test.tfrecords` ファイルに出力します。観測記録はそれぞれ `tf.Example` メッセージに変換され、ファイルに出力されます。その後、`test.tfrecords` ファイルが作成されたことを確認することができます。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "MKPHzoGv7q44",
        "colab": {}
      },
      "source": [
        "# `tf.Example` 観測記録をファイルに出力\n",
        "with tf.io.TFRecordWriter(filename) as writer:\n",
        "  for i in range(n_observations):\n",
        "    example = serialize_example(feature0[i], feature1[i], feature2[i], feature3[i])\n",
        "    writer.write(example)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "EjdFHHJMpUUo",
        "colab": {}
      },
      "source": [
        "!du -sh {filename}"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "wtQ7k0YWQ1cz"
      },
      "source": [
        "### TFRecord ファイルの読み込み\n",
        "\n",
        "これらのシリアライズされたテンソルは、`tf.train.Example.ParseFromString` を使って簡単にパースできます。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "36ltP9B8OezA",
        "colab": {}
      },
      "source": [
        "filenames = [filename]\n",
        "raw_dataset = tf.data.TFRecordDataset(filenames)\n",
        "raw_dataset"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "BpS-R4MLfcic",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "for raw_record in raw_dataset.take(1):\n",
        "  example = tf.train.Example()\n",
        "  example.ParseFromString(raw_record.numpy())\n",
        "  print(example)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "S0tFDrwdoj3q"
      },
      "source": [
        "## ウォークスルー： 画像データの読み書き"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "rjN2LFxFpcR9"
      },
      "source": [
        "以下は、TFRecord を使って画像データを読み書きする方法の例です。この例の目的は、データ（この場合は画像）を入力し、そのデータを TFRecord ファイルに書き込んで、再びそのファイルを読み込み、画像を表示するという手順を最初から最後まで示すことです。\n",
        "\n",
        "これは、たとえば、おなじ入力データセットを使って複数のモデルを構築するといった場合に役立ちます。画像データをそのまま保存する代わりに、TFRecord 形式に前処理しておき、その後の処理やモデル構築に使用することができます。\n",
        "\n",
        "まずは、雪の中の猫の[画像](https://commons.wikimedia.org/wiki/File:Felis_catus-cat_on_snow.jpg)と、ニューヨーク市にあるウイリアムズバーグ橋の [写真](https://upload.wikimedia.org/wikipedia/commons/f/fe/New_East_River_Bridge_from_Brooklyn_det.4a09796u.jpg)をダウンロードしましょう。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "5Lk2qrKvN0yu"
      },
      "source": [
        "### 画像の取得"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "3a0fmwg8lHdF",
        "colab": {}
      },
      "source": [
        "cat_in_snow  = tf.keras.utils.get_file('320px-Felis_catus-cat_on_snow.jpg', 'https://storage.googleapis.com/download.tensorflow.org/example_images/320px-Felis_catus-cat_on_snow.jpg')\n",
        "williamsburg_bridge = tf.keras.utils.get_file('194px-New_East_River_Bridge_from_Brooklyn_det.4a09796u.jpg','https://storage.googleapis.com/download.tensorflow.org/example_images/194px-New_East_River_Bridge_from_Brooklyn_det.4a09796u.jpg')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "7aJJh7vENeE4",
        "colab": {}
      },
      "source": [
        "display.display(display.Image(filename=cat_in_snow))\n",
        "display.display(display.HTML('Image cc-by: <a \"href=https://commons.wikimedia.org/wiki/File:Felis_catus-cat_on_snow.jpg\">Von.grzanka</a>'))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "KkW0uuhcXZqA",
        "colab": {}
      },
      "source": [
        "display.display(display.Image(filename=williamsburg_bridge))\n",
        "display.display(display.HTML('<a \"href=https://commons.wikimedia.org/wiki/File:New_East_River_Bridge_from_Brooklyn_det.4a09796u.jpg\">From Wikimedia</a>'))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "VSOgJSwoN5TQ"
      },
      "source": [
        "### TFRecord ファイルの書き出し"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Azx83ryQEU6T"
      },
      "source": [
        "上記で行ったように、この特徴量を `tf.Example` と互換のデータ型にエンコードできます。この場合には、生の画像文字列を特徴として保存するだけではなく、縦、横のサイズにチャネル数、更に画像を保存する際に猫の画像と橋の画像を区別するための `label` 特徴量を付け加えます。猫の画像には `0` を、橋の画像には `1` を使うことにしましょう。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "kC4TS1ZEONHr",
        "colab": {}
      },
      "source": [
        "image_labels = {\n",
        "    cat_in_snow : 0,\n",
        "    williamsburg_bridge : 1,\n",
        "}"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "c5njMSYNEhNZ",
        "colab": {}
      },
      "source": [
        "# 猫の画像を使った例\n",
        "image_string = open(cat_in_snow, 'rb').read()\n",
        "\n",
        "label = image_labels[cat_in_snow]\n",
        "\n",
        "# 関連する特徴量のディクショナリを作成\n",
        "def image_example(image_string, label):\n",
        "  image_shape = tf.image.decode_jpeg(image_string).shape\n",
        "\n",
        "  feature = {\n",
        "      'height': _int64_feature(image_shape[0]),\n",
        "      'width': _int64_feature(image_shape[1]),\n",
        "      'depth': _int64_feature(image_shape[2]),\n",
        "      'label': _int64_feature(label),\n",
        "      'image_raw': _bytes_feature(image_string),\n",
        "  }\n",
        "\n",
        "  return tf.train.Example(features=tf.train.Features(feature=feature))\n",
        "\n",
        "for line in str(image_example(image_string, label)).split('\\n')[:15]:\n",
        "  print(line)\n",
        "print('...')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "2G_o3O9MN0Qx"
      },
      "source": [
        "ご覧のように、すべての特徴量が `tf.Example` メッセージに保存されました。上記のコードを関数化し、このサンプルメッセージを `images.tfrecords` ファイルに書き込みます。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "qcw06lQCOCZU",
        "colab": {}
      },
      "source": [
        "# 生の画像を images.tfrecords ファイルに書き出す\n",
        "# まず、2つの画像を tf.Example メッセージに変換し、\n",
        "# 次に .tfrecords ファイルに書き出す\n",
        "record_file = 'images.tfrecords'\n",
        "with tf.io.TFRecordWriter(record_file) as writer:\n",
        "  for filename, label in image_labels.items():\n",
        "    image_string = open(filename, 'rb').read()\n",
        "    tf_example = image_example(image_string, label)\n",
        "    writer.write(tf_example.SerializeToString())"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "yJrTe6tHPCfs",
        "colab": {}
      },
      "source": [
        "!du -sh {record_file}"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "jJSsCkZLPH6K"
      },
      "source": [
        "### TFRecord ファイルの読み込み\n",
        "\n",
        "これで、`images.tfrecords` ファイルができました。このファイルの中のレコードをイテレートし、書き込んだものを読み出します。このユースケースでは、画像を復元するだけなので、必要なのは生画像の文字列だけです。上記のゲッター、すなわち、`example.features.feature['image_raw'].bytes_list.value[0]` を使って抽出することができます。猫と橋のどちらであるかを決めるため、ラベルも使用します。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "M6Cnfd3cTKHN",
        "colab": {}
      },
      "source": [
        "raw_image_dataset = tf.data.TFRecordDataset('images.tfrecords')\n",
        "\n",
        "# 特徴量を記述するディクショナリを作成\n",
        "image_feature_description = {\n",
        "    'height': tf.io.FixedLenFeature([], tf.int64),\n",
        "    'width': tf.io.FixedLenFeature([], tf.int64),\n",
        "    'depth': tf.io.FixedLenFeature([], tf.int64),\n",
        "    'label': tf.io.FixedLenFeature([], tf.int64),\n",
        "    'image_raw': tf.io.FixedLenFeature([], tf.string),\n",
        "}\n",
        "\n",
        "def _parse_image_function(example_proto):\n",
        "  # 入力の tf.Example のプロトコルバッファを上記のディクショナリを使って解釈\n",
        "  return tf.io.parse_single_example(example_proto, image_feature_description)\n",
        "\n",
        "parsed_image_dataset = raw_image_dataset.map(_parse_image_function)\n",
        "parsed_image_dataset"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "0PEEFPk4NEg1"
      },
      "source": [
        "TFRecord ファイルから画像を復元しましょう。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "yZf8jOyEIjSF",
        "colab": {}
      },
      "source": [
        "for image_features in parsed_image_dataset:\n",
        "  image_raw = image_features['image_raw'].numpy()\n",
        "  display.display(display.Image(data=image_raw))"
      ],
      "execution_count": 0,
      "outputs": []
    }
  ]
}
